package com.staples.copyprint.webservice.util;

import java.awt.Color;
import java.io.IOException;
import java.util.List;
import java.util.ResourceBundle;

import org.apache.log4j.Logger;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font;

import com.staples.copyprint.webservice.domain.OrderTicketLayout;
import com.staples.copyprint.webservice.domain.SectionDetails;
import com.staples.copyprint.webservice.model.PDFBean;
import com.staples.copyprint.webservice.service.InsufficientConfigurationException;

/**
 * Class Name - PDFUtil 
 * This class is used to to do all OderTicket
 * pdf related stuffs. It is responsible for 
 * creation and saving of OderTicket 
 * 
 * @author Cognizant Technology Solutions
 */
public class PDFUtil {
	
	private static final float PAGE_MARGIN = 20;
	private static final float LEGEND_PADDING = 5;
	private static final float GAP = 10;
	private static final float SECTION_ROW_HEIGHT = 16;
	
	private static final Logger logger = Logger.getLogger(PDFUtil.class);
	private static ResourceBundle bundle = CDSResourceBundle.CDSResourceProperties();

	/**
	 * This method is mainly responsible for creation
	 * and saving of pdf
	 * 
	 * @param PDFBean - data structure to draw PDF
	 * @param isFirstPage - flag to denote first page
	 * @param isLastPage - flag to denote last page
	 * 
	 * @throws Exception
	 * */
	public static void createPDF(PDDocument doc,PDFBean pdfBean,boolean isFirstPage, boolean isLastPage) throws Exception {
		
		logger.info("----->Entering into PDFUtil.createPDF()");
		
		try {
			
			List<OrderTicketLayout> sectionList = pdfBean.getSectionList();
			logger.info("----->Sectionlist size: "+sectionList.size());
			
			//Add page to the doc
			PDPage page = new PDPage();
			doc.addPage( page );
			
			//Define the content stream
			PDFont font = PDType1Font.HELVETICA;
			PDPageContentStream contentStream = new PDPageContentStream(doc, page);
			contentStream.setFont(font, 11);
			
			//Get page width & height
			float pageWidth = page.findMediaBox().getWidth();
			float pageHeight = page.findMediaBox().getHeight();
			float usableY = pageHeight - PAGE_MARGIN;
			int counter=0;
			
			//Add customer & schedule section for only first page
			if(isFirstPage){
				OrderTicketLayout customer = sectionList.get(0);
				OrderTicketLayout schedule = sectionList.get(1);
				usableY = addTopSection(contentStream, pageWidth, pageHeight, customer,schedule,pdfBean);
				counter=2;
			}
			logger.info("----->Customer & Schedule sections are added");
			
			//Add others sections
			boolean isFirstSection = true;
			for(int i=counter; i<sectionList.size(); i++){
				
				OrderTicketLayout section = sectionList.get(i);
				
				//Get effective size, there can be object without values
				int effectiveSize = getEffectiveListSize(section.getSectionDetailsList());
				logger.info("----->Effective size for section "+i+" is: "+effectiveSize);
				
				if(effectiveSize>0){//There should be at least one label with value
					float sectionHeight = (effectiveSize * SECTION_ROW_HEIGHT) +50;
					
					//If section height is not accomodated within 
					//available space, add a new page
					if(usableY-sectionHeight< PAGE_MARGIN){
						contentStream.close();
						
						page = new PDPage();
						doc.addPage( page );
						contentStream = new PDPageContentStream(doc, page);
						contentStream.setFont(font, 11);
						usableY = pageHeight - PAGE_MARGIN;
					}
					
					//Add a section
					usableY = addSection(contentStream, pageWidth, pageHeight,usableY,section,pdfBean,isFirstSection);
					isFirstSection = false;
				}
			}
			
			contentStream.close();
			
			//Save the document for last line
			if(isLastPage){
				String fileName = bundle.getString("pdfRootPath") + pdfBean.getOrderNumber()+".pdf";
				logger.info("----->Going to save the PDF at:"+fileName);
				logger.debug("----->Going to save the PDF at:"+fileName);
				
				doc.save(fileName);
				doc.close();
			}
		} catch (Exception e) {
			logger.error("-----> Exception in PDFUtil.createPDF(): ", e);
			throw new Exception(e.getMessage());
		}
		
		logger.info("----->Leaving from PDFUtil.createPDF()");
	}
	
	/**
	 * This method is responsible for adding
	 * top section containing Customer & Schedule
	 * 
	 * @param PDPageContentStream 
	 * @param pageWidth 
	 * @param pageHeight 
	 * @param customer
	 * @param schedule
	 * @param pdfBean
	 * 
	 * @return usableY
	 * @throws IOException
	 * */
	private static float addTopSection( PDPageContentStream contentStream, float pageWidth, float pageHeight, OrderTicketLayout customer, OrderTicketLayout schedule, PDFBean pdfBean) throws IOException{
		
		logger.info("----->Entering into PDFUtil.addTopSection()");
		
		//Get customer details & schedule details
		List<SectionDetails> customerDetailsList = customer.getSectionDetailsList();
		List<SectionDetails> scheduleDetailsList = schedule.getSectionDetailsList();	
		
		PDFont font = PDType1Font.HELVETICA;
		float usableY = pageHeight - PAGE_MARGIN;
		
		//Write pdf heading,currently "orderticket"
		contentStream.beginText();
        contentStream.moveTextPositionByAmount( PAGE_MARGIN, usableY );
        contentStream.drawString(pdfBean.getDocHeading());
        contentStream.endText();
        usableY -= GAP;
        contentStream.drawLine(PAGE_MARGIN, usableY, pageWidth-PAGE_MARGIN, usableY);
        usableY -= GAP;
        
        //write order no. ribon
        contentStream.setNonStrokingColor( 135,135,135);
        float[] arrX = {PAGE_MARGIN,pageWidth-PAGE_MARGIN,pageWidth-PAGE_MARGIN,PAGE_MARGIN};
        float[] arrY = {usableY,usableY,usableY-20,usableY-20};
        contentStream.fillPolygon(arrX,arrY);
        
        //Write order no.
        contentStream.beginText();
        contentStream.setNonStrokingColor( Color.WHITE);
        contentStream.moveTextPositionByAmount( PAGE_MARGIN+LEGEND_PADDING, usableY-15 );
        contentStream.drawString("Order Number: "+pdfBean.getOrderNumber());
        contentStream.endText();
        usableY -= GAP+20;
        
        float boxHeight=160;
        //Draw top left box section
        contentStream.setStrokingColor( 189,207,244 );
        float[] arrLBoxX = {PAGE_MARGIN,PAGE_MARGIN+1,PAGE_MARGIN+2,PAGE_MARGIN+4,pageWidth/2-GAP/2-4,pageWidth/2-GAP/2-2,pageWidth/2-GAP/2-1,pageWidth/2-GAP/2,pageWidth/2-GAP/2,PAGE_MARGIN};
        float[] arrLBoxY = {usableY-4,usableY-2,usableY-1,usableY,usableY,usableY-1,usableY-2,usableY-4,usableY-boxHeight,usableY-boxHeight};
        contentStream.drawPolygon(arrLBoxX,arrLBoxY);
        
        //Draw rounded cornered header on top left box
        contentStream.setNonStrokingColor( 189,207,244 );
        float[] arrLBoxHeaderX = {PAGE_MARGIN,PAGE_MARGIN+1,PAGE_MARGIN+2,PAGE_MARGIN+4,pageWidth/2-GAP/2-4,pageWidth/2-GAP/2-2,pageWidth/2-GAP/2-1,pageWidth/2-GAP/2,pageWidth/2-GAP/2,PAGE_MARGIN};
        float[] arrLBoxHeaderY = {usableY-4,usableY-2,usableY-1,usableY,usableY,usableY-1,usableY-2,usableY-4,usableY-25,usableY-25};
        contentStream.fillPolygon(arrLBoxHeaderX,arrLBoxHeaderY);
        
        //Write customer title
        contentStream.beginText();
        contentStream.setNonStrokingColor(14,47,59);
        contentStream.moveTextPositionByAmount( PAGE_MARGIN+LEGEND_PADDING+10, usableY-15 );
        contentStream.drawString(customer.getSectionTitle());
        contentStream.endText();
        
        float leftBoxLegendPos = usableY - 40;
        
        contentStream.setFont(font, 10);
		
        //Print legend and value for customer details
		for(int i=0; i<customerDetailsList.size(); i++){
			SectionDetails details = customerDetailsList.get(i);
			
			//Add legend
	        contentStream.beginText();
	        contentStream.moveTextPositionByAmount( PAGE_MARGIN+LEGEND_PADDING, leftBoxLegendPos );
	        contentStream.drawString(details.getLegend()+": ");
	        contentStream.endText();
	        
	        //Wrap text if content is long.e.g. address
	        float txtValHeight = wrapTextAndWrite(contentStream,10,200,details.getItemValue(),PAGE_MARGIN+LEGEND_PADDING+100, leftBoxLegendPos);
	        if(txtValHeight == leftBoxLegendPos){
	        	leftBoxLegendPos-=SECTION_ROW_HEIGHT;
	        }else{
	        	leftBoxLegendPos=txtValHeight - SECTION_ROW_HEIGHT;
	        }
		}
        
        
        //Add top right box section
        contentStream.setStrokingColor( 189,207,244 );
        float[] arrRBoxX = {pageWidth/2+GAP,pageWidth/2+GAP+1,pageWidth/2+GAP+2,pageWidth/2+GAP+4,pageWidth-PAGE_MARGIN-4,pageWidth-PAGE_MARGIN-2,pageWidth-PAGE_MARGIN-1,pageWidth-PAGE_MARGIN,pageWidth-PAGE_MARGIN,pageWidth/2+GAP};
        float[] arrRBoxY = {usableY-4,usableY-2,usableY-1,usableY,usableY,usableY-1,usableY-2,usableY-4,usableY-boxHeight,usableY-boxHeight};
        contentStream.drawPolygon(arrRBoxX,arrRBoxY);
        
        //Draw rounded cornered header on top right box section
        contentStream.setNonStrokingColor( 189,207,244 );
        float[] arrRBoxHeaderX = {pageWidth/2+GAP,pageWidth/2+GAP+1,pageWidth/2+GAP+2,pageWidth/2+GAP+4,pageWidth-PAGE_MARGIN-4,pageWidth-PAGE_MARGIN-2,pageWidth-PAGE_MARGIN-1,pageWidth-PAGE_MARGIN,pageWidth-PAGE_MARGIN,pageWidth/2+GAP};
        float[] arrRBoxHeaderY = {usableY-4,usableY-2,usableY-1,usableY,usableY,usableY-1,usableY-2,usableY-4,usableY-25,usableY-25};
        contentStream.fillPolygon(arrRBoxHeaderX,arrRBoxHeaderY);
        
        //Write schedule title
        contentStream.beginText();
        contentStream.setFont(font, 11);
        contentStream.setNonStrokingColor(14,47,59);
        contentStream.moveTextPositionByAmount( pageWidth/2+GAP+10, usableY-15 );
        contentStream.drawString(schedule.getSectionTitle());
        contentStream.endText();
        
        float rightBoxLegendPos = usableY - 40;
        
        contentStream.setFont(font, 10);
		
        //Print legend and value for schedule details
		for(int i=0; i<scheduleDetailsList.size(); i++){
			SectionDetails details = scheduleDetailsList.get(i);
			
			//Add legend
	        contentStream.beginText();
	        contentStream.moveTextPositionByAmount( pageWidth/2+GAP+LEGEND_PADDING, rightBoxLegendPos );
	        contentStream.drawString(details.getLegend()+": ");
	        contentStream.endText();
	        
	        //Add value
	        contentStream.beginText();
        	contentStream.moveTextPositionByAmount( pageWidth/2+GAP+LEGEND_PADDING+100, rightBoxLegendPos );
	        contentStream.drawString(details.getItemValue());
	        contentStream.endText();
	        
	        rightBoxLegendPos-=SECTION_ROW_HEIGHT;
		}
        
		contentStream.setFont(PDType1Font.HELVETICA, 11);
		
        //Line and grey strip
        usableY -= boxHeight+5;
        contentStream.setStrokingColor( Color.BLACK );
        contentStream.drawLine(PAGE_MARGIN, usableY, pageWidth-PAGE_MARGIN, usableY);
        usableY -= GAP;
        
        logger.info("----->Leaving from PDFUtil.addTopSection()");
		return usableY;
	}
	
	/**
	 * This method is responsible for adding
	 * each section with section details
	 * 
	 * @param PDPageContentStream 
	 * @param pageWidth 
	 * @param pageHeight 
	 * @param usableY
	 * @param section
	 * @param pdfBean
	 * 
	 * @return usableY
	 * @throws IOException
	 * */
	private static float addSection(PDPageContentStream contentStream, float pageWidth, float pageHeight,float usableY, OrderTicketLayout section, PDFBean pdfBean, boolean isFirstSection) throws IOException{
		logger.info("----->Entering into PDFUtil.addSection()");
		
		PDFont font = PDType1Font.HELVETICA;
		contentStream.setFont(font, 11);
		
		//Add job title ribon only for first section
		if(isFirstSection){
			//Black ribon & job text
			contentStream.setNonStrokingColor( 135,135,135);
	        float[] arr2X = {PAGE_MARGIN,pageWidth-PAGE_MARGIN,pageWidth-PAGE_MARGIN,PAGE_MARGIN};
	        float[] arr2Y = {usableY,usableY,usableY-20,usableY-20};
	        contentStream.fillPolygon(arr2X,arr2Y);
	        
	        //Add job count text
	        contentStream.beginText();
	        contentStream.setNonStrokingColor(14,47,59);
	        contentStream.moveTextPositionByAmount( PAGE_MARGIN+LEGEND_PADDING, usableY-15 );
	        contentStream.drawString("Job "+pdfBean.getThisJobNo()+" of "+pdfBean.getTotalJobs());
	        contentStream.endText();
	        
	        //Add job title text
	        contentStream.beginText();
	        float right = font.getStringWidth("Job Title: "+pdfBean.getJobTitle())/1000*11+5;
	        contentStream.moveTextPositionByAmount( pageWidth-PAGE_MARGIN-right, usableY-15 );
	        contentStream.drawString("Job Title: "+pdfBean.getJobTitle());
	        contentStream.endText();
	        usableY -= GAP+25;
		}
		
		//Title text
		String header = section.getSectionTitle();
		contentStream.beginText();
        contentStream.setNonStrokingColor(14,47,59);
        contentStream.moveTextPositionByAmount( PAGE_MARGIN, usableY );
        contentStream.drawString(header);
        contentStream.endText();
        
        usableY -= GAP;
        List<SectionDetails> sectionDetailsList = section.getSectionDetailsList();
        int effectiveSize = getEffectiveListSize(sectionDetailsList);
        float secContainerHeight = effectiveSize * SECTION_ROW_HEIGHT + 50;
        
        //Add section container
		contentStream.setStrokingColor( 189,207,244 );
        float[] arrLBoxX = {PAGE_MARGIN,PAGE_MARGIN+1,PAGE_MARGIN+2,PAGE_MARGIN+4,pageWidth-PAGE_MARGIN-4,pageWidth-PAGE_MARGIN-2,pageWidth-PAGE_MARGIN-1,pageWidth-PAGE_MARGIN,pageWidth-PAGE_MARGIN,PAGE_MARGIN};
        float[] arrLBoxY = {usableY-4,usableY-2,usableY-1,usableY,usableY,usableY-1,usableY-2,usableY-4,usableY-secContainerHeight,usableY-secContainerHeight};
        contentStream.drawPolygon(arrLBoxX,arrLBoxY);
        
        //Draw rounded cornered header on section container
        contentStream.setNonStrokingColor( 189,207,244 );
        float[] arrLBoxHeaderX = {PAGE_MARGIN,PAGE_MARGIN+1,PAGE_MARGIN+2,PAGE_MARGIN+4,pageWidth-PAGE_MARGIN-4,pageWidth-PAGE_MARGIN-2,pageWidth-PAGE_MARGIN-1,pageWidth-PAGE_MARGIN,pageWidth-PAGE_MARGIN,PAGE_MARGIN};
        float[] arrLBoxHeaderY = {usableY-4,usableY-2,usableY-1,usableY,usableY,usableY-1,usableY-2,usableY-4,usableY-25,usableY-25};
        contentStream.fillPolygon(arrLBoxHeaderX,arrLBoxHeaderY);
        
        float legendPos = usableY - 40;
        
		contentStream.setFont(font, 9);
		
        //Print legend and value for section details
		for(int i=0; i<sectionDetailsList.size(); i++){
			SectionDetails details = sectionDetailsList.get(i);
			String text = details.getItemValue().trim();
			text = text.replaceAll("(\\r|\\n|\\t)", "");
			
			if(text != "" && text != null){ // Don't include the legend if value is not present
				if(details.getLegend() != ""){
			        contentStream.beginText();
			        contentStream.setNonStrokingColor( 70,70,70 );
			        contentStream.moveTextPositionByAmount( PAGE_MARGIN+LEGEND_PADDING, legendPos );
			        contentStream.drawString(details.getLegend()+": ");
			        contentStream.endText();
				}
		        
				float moveX = 0;
				float txtWidth = pageWidth - PAGE_MARGIN - GAP -170;
		        if(details.getLegend()==""){// When legend is blank, start from beginning
		        	moveX = PAGE_MARGIN+LEGEND_PADDING;
		        	txtWidth = pageWidth - PAGE_MARGIN - GAP;
		        }else{
		        	moveX = PAGE_MARGIN+LEGEND_PADDING+150;
		        }
		        contentStream.setNonStrokingColor( 70,70,70 );
		       
		        
		        
		        if((font.getStringWidth(text) / 1000 * 9) >txtWidth ){ //Wrap text if content much
		        	legendPos = wrapTextAndWrite(contentStream,9,txtWidth,text,moveX,legendPos);
		        }else{
		        	contentStream.beginText();
			        contentStream.moveTextPositionByAmount( moveX, legendPos );
			        contentStream.drawString(text);
			        contentStream.endText();
		        }
		       
		        legendPos-=SECTION_ROW_HEIGHT;
			}
		}
        
        usableY -= secContainerHeight+SECTION_ROW_HEIGHT;
        
        logger.info("----->Leaving from PDFUtil.addSection()");
        return usableY;
	}
	
	/**
	 * This method is responsible for 
	 * wrapping up data into multiple lines
	 * 
	 * @param PDPageContentStream 
	 * @param fontSize 
	 * @param paragraphWidth 
	 * @param text
	 * @param moveX
	 * @param moveY
	 * 
	 * @return height
	 * @throws IOException
	 * */
	private static float wrapTextAndWrite(PDPageContentStream content, int fontSize,float paragraphWidth,String text,float moveX,float moveY) throws IOException{
		logger.info("----->Entering into PDFUtil.wrapTextAndWrite()");
		
		PDFont font = PDType1Font.HELVETICA; 
		int start = 0;
		int end = 0;
		float height = moveY;
		for ( int i : possibleWrapPoints(text) ) {
		    float width = font.getStringWidth(text.substring(start,i)) / 1000 * fontSize;
		    if ( start < end && width > paragraphWidth ) {
		        // Draw partial text and increase height
		    	content.beginText();
		        content.moveTextPositionByAmount(moveX , height);
		        content.drawString(text.substring(start,end));
		        content.endText();
		        height -= font.getFontBoundingBox().getHeight() / 1000 * fontSize;
		        start = end;
		    }
		    end = i;
		}
		
		// Last piece of text
		content.beginText();
		content.moveTextPositionByAmount(moveX , height);
		content.drawString(text.substring(start));
		content.endText();
		
		logger.info("----->Leaving from PDFUtil.wrapTextAndWrite()");
		return height;
	}
	
	/**
	 * This method is responsible for 
	 * determining probable wrap points
	 * 
	 * @param text 
	 * 
	 * @return int[]
	 * */
	private static int[] possibleWrapPoints(String text) {
		logger.info("----->Entering into PDFUtil.possibleWrapPoints()");
		
	    String[] split = text.split("(?<=\\W)");
	    int[] ret = new int[split.length];
	    ret[0] = split[0].length();
	    for ( int i = 1 ; i < split.length ; i++ )
	        ret[i] = ret[i-1] + split[i].length();
	    logger.info("ret--->"+ ret.length);
	    
	    logger.info("----->Leaving from PDFUtil.possibleWrapPoints()");
	    return ret;
	}
	
	/**
	 * This method is responsible for 
	 * calculating row count.This is required to
	 * to determine section box height.
	 * It does not count the object without value. 
	 * 
	 * @param sectionDetailsList 
	 * @return count
	 * */
	private static int getEffectiveListSize(List<SectionDetails> sectionDetailsList){
		logger.info("----->Entering into PDFUtil.getEffectiveListSize()");
		int count=0;
		for(SectionDetails sectionDetails: sectionDetailsList){
			if(sectionDetails.getItemValue() !="" && sectionDetails.getItemValue() != null){
				count++;
			}
		}
		logger.info("----->Leaving from PDFUtil.getEffectiveListSize()");
		return count;
	}
	
}
